#include "fs.h"
#include "disk.h"
#include "sync.h"
#include <string.h>
#include <stdio.h>
#include <libgen.h>

SuperBlock super_block;  // 全局变量保存挂载后的超级块

int fs_format() {
    void *base = disk_get_base();
    if (!base) {
        printf("磁盘未初始化，无法格式化。\n");
        return -1;
    }

    memset(base, 0, DISK_SIZE);  // 清空整个磁盘映像

    // 计算关键位置
    uint32_t inode_bitmap_blk = 1;
    uint32_t data_bitmap_blk  = inode_bitmap_blk + 2;     // 保留2块给 inode bitmap
    uint32_t inode_table_blk  = data_bitmap_blk + 4;      // 保留4块给 data bitmap
    uint32_t inode_table_size = 1024 * sizeof(Inode);     // 假设 Inode 是 64 字节
    uint32_t inode_table_blks = inode_table_size / BLOCK_SIZE; // = 16
    uint32_t data_block_start = inode_table_blk + inode_table_blks;

    // 1. 初始化超级块
    SuperBlock *sb = (SuperBlock *)base;
    sb->magic = FS_MAGIC;
    sb->total_blocks = DISK_SIZE / BLOCK_SIZE;

    // 磁盘结构：0: 超级块, 1-2: inode bitmap, 3-6: data bitmap
    sb->inode_bitmap_start = inode_bitmap_blk;
    sb->data_bitmap_start = data_bitmap_blk;
    sb->inode_table_start  = inode_table_blk;
    sb->data_block_start   = data_block_start;

    // 同步全局变量
    super_block = *sb;

    // 2. 初始化位图为0（已由 memset 完成）

    // 3. 设置 inode 0 为根目录
    Inode *inode_table = (Inode *)(base + sb->inode_table_start * BLOCK_SIZE);
    Inode *root_inode = &inode_table[0];
    root_inode->type = FILE_TYPE_DIR;
    root_inode->size = sizeof(DirEntry) * 2;

    // 分配数据块给根目录（块号 = 23）
    uint32_t root_blk = sb->data_block_start;
    root_inode->direct_ptr[0] = root_blk;
    for (int i = 1; i < INODE_DIRECT_PTRS; ++i) root_inode->direct_ptr[i] = 0;

    // 4. 写入 . 和 .. 到根目录数据块
    DirEntry *dir = (DirEntry *)(base + root_blk * BLOCK_SIZE);
    strcpy(dir[0].name, ".");
    dir[0].inode_id = 0;
    strcpy(dir[1].name, "..");
    dir[1].inode_id = 0;

    // 5. 设置 inode bitmap（标记 inode 0 已用）
    uint8_t *inode_bitmap = (uint8_t *)(base + sb->inode_bitmap_start * BLOCK_SIZE);
    inode_bitmap[0] |= 0x01;

    // 6. 设置 data bitmap（标记 block 0 已用 → 相对 data_block_start）
    uint8_t *data_bitmap = (uint8_t *)(base + sb->data_bitmap_start * BLOCK_SIZE);
    int relative_blk = root_blk - sb->data_block_start;
    data_bitmap[relative_blk / 8] |= (1 << (relative_blk % 8));

    return 0;
}
int fs_mount() {
    void *base = disk_get_base();  // 获取 mmap 映射的磁盘首地址
    if (!base) {
        printf("磁盘未初始化。\n");
        return -1;
    }

    // 从 Block 0 读取超级块
    memcpy(&super_block, base, sizeof(SuperBlock));

    // 检查 magic 值是否正确
    if (super_block.magic != FS_MAGIC) {
        printf("挂载失败：文件系统未格式化或格式不匹配。\n");
        return -1;
    }

    sync_init(); // 初始化文件锁表

    return 0;
}
int fs_create_dir(const char *path) {
    if (strcmp(path, "/") == 0) {
        printf("错误：根目录已存在。\n");
        return -1;
    }

    char parent_path[128], name[128];
    if (path_parent(path, parent_path, name) != 0) {
        printf("路径拆分失败。\n");
        return -1;
    }

    int parent_inode_id = path_lookup(parent_path);
    if (parent_inode_id < 0) {
        printf("父目录不存在：%s\n", parent_path);
        return -1;
    }

    // 判断目标是否已存在
    if (path_lookup(path) >= 0) {
        printf("目标目录已存在：%s\n", path);
        return -1;
    }

    // 分配 inode 和数据块
    int new_inode_id = alloc_inode();
    int new_block_id = alloc_block();
    if (new_inode_id < 0 || new_block_id < 0) {
        printf("分配资源失败。\n");
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *new_inode = &inode_table[new_inode_id];

    // 初始化新目录 inode
    new_inode->type = FILE_TYPE_DIR;
    new_inode->size = sizeof(DirEntry) * 2;  // . 和 ..
    new_inode->direct_ptr[0] = new_block_id;
    for (int i = 1; i < INODE_DIRECT_PTRS; ++i) {
        new_inode->direct_ptr[i] = 0;
    }

    // 写入 . 和 .. 两个目录项
    DirEntry *dir = (DirEntry *)(base + new_block_id * BLOCK_SIZE);
    strcpy(dir[0].name, ".");
    dir[0].inode_id = new_inode_id;
    strcpy(dir[1].name, "..");
    dir[1].inode_id = parent_inode_id;
    new_inode->size = sizeof(DirEntry) * 2;

    // 将新目录加入到父目录中
    if (dir_add_entry(parent_inode_id, name, new_inode_id) < 0) {
        printf("错误：添加目录项失败。\n");
        free_inode(new_inode_id);
        free_block(new_block_id);
        return -1;
    }

    printf("目录创建成功：%s\n", path);
    return 0;
}
int fs_list_dir(const char *path) {
    int inode_id = path_lookup(path);
    if (inode_id < 0) {
        printf("错误：路径不存在：%s\n", path);
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *dir_inode = &inode_table[inode_id];

    if (dir_inode->type != FILE_TYPE_DIR) {
        printf("错误：目标不是目录：%s\n", path);
        return -1;
    }

    printf("目录 %s 内容：\n", path);

    for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
        uint32_t blk = dir_inode->direct_ptr[i];
        if (blk == 0) continue;

        DirEntry *entries = (DirEntry *)(base + blk * BLOCK_SIZE);
        for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
            if (strlen(entries[j].name) > 0) {
                printf("%s (inode=%d)\n", entries[j].name, entries[j].inode_id);
            }
        }
    }


    return 0;
}
int fs_create_file(const char *path) {
    if (strcmp(path, "/") == 0) {
        printf("错误：不能在根路径创建文件。\n");
        return -1;
    }

    // 1. 路径拆分为父目录 + 文件名
    char parent_path[128], name[128];
    if (path_parent(path, parent_path, name) != 0) {
        printf("路径拆分失败。\n");
        return -1;
    }

    // 2. 找到父目录 inode
    int parent_inode_id = path_lookup(parent_path);
    if (parent_inode_id < 0) {
        printf("父目录不存在：%s\n", parent_path);
        return -1;
    }

    // 3. 如果文件已存在，则报错
    if (path_lookup(path) >= 0) {
        printf("文件已存在：%s\n", path);
        return -1;
    }

    // 4. 分配 inode 和数据块（数据块可推迟写入）
    int new_inode_id = alloc_inode();
    int new_block_id = alloc_block();  // 可以延迟分配，暂时先分一个空的
    if (new_inode_id < 0 || new_block_id < 0) {
        printf("资源分配失败。\n");
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *new_inode = &inode_table[new_inode_id];

    // 5. 初始化新 inode 为文件
    new_inode->type = FILE_TYPE_FILE;
    new_inode->size = 0;
    new_inode->direct_ptr[0] = new_block_id;
    for (int i = 1; i < INODE_DIRECT_PTRS; ++i)
        new_inode->direct_ptr[i] = 0;

    // 6. 将文件加入父目录
    if (dir_add_entry(parent_inode_id, name, new_inode_id) < 0) {
        printf("添加目录项失败。\n");
        free_inode(new_inode_id);
        free_block(new_block_id);
        return -1;
    }

    printf("文件创建成功：%s\n", path);
    return 0;
}
int fs_write_file(const char *path, const void *buf, size_t len) {
    int inode_id = path_lookup(path);
    if (inode_id < 0) {
        printf("错误：文件不存在：%s\n", path);
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *inode = &inode_table[inode_id];

    if (inode->type != FILE_TYPE_FILE) {
        printf("错误：目标不是文件：%s\n", path);
        return -1;
    }

    const uint8_t *data = (const uint8_t *)buf;
    size_t written = 0;

    for (int i = 0; i < INODE_DIRECT_PTRS && written < len; ++i) {
        if (inode->direct_ptr[i] == 0) {
            int blk = alloc_block();
            if (blk < 0) {
                printf("写入失败：磁盘空间不足。\n");
                return -1;
            }
            inode->direct_ptr[i] = blk;
        }

        int blk_id = inode->direct_ptr[i];
        uint8_t *block_addr = (uint8_t *)(base + blk_id * BLOCK_SIZE);

        size_t to_write = len - written;
        if (to_write > BLOCK_SIZE) to_write = BLOCK_SIZE;

        memcpy(block_addr, data + written, to_write);
        written += to_write;
    }

    inode->size = written;
    printf("写入成功：%s，写入字节数：%zu\n", path, written);
    return 0;
}
int fs_read_file(const char *path, void *buf, size_t len) {
    int inode_id = path_lookup(path);
    if (inode_id < 0) {
        printf("错误：文件不存在：%s\n", path);
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *inode = &inode_table[inode_id];

    if (inode->type != FILE_TYPE_FILE) {
        printf("错误：目标不是文件：%s\n", path);
        return -1;
    }

    uint8_t *dst = (uint8_t *)buf;
    size_t file_size = inode->size;
    size_t to_read = file_size < len ? file_size : len;
    size_t read_total = 0;

    for (int i = 0; i < INODE_DIRECT_PTRS && read_total < to_read; ++i) {
        int blk_id = inode->direct_ptr[i];
        if (blk_id == 0) break;

        uint8_t *block = (uint8_t *)(base + blk_id * BLOCK_SIZE);

        size_t remaining = to_read - read_total;
        size_t chunk = (remaining > BLOCK_SIZE) ? BLOCK_SIZE : remaining;

        memcpy(dst + read_total, block, chunk);
        read_total += chunk;
    }

    printf("读取成功：%s，字节数：%zu\n", path, read_total);
    return read_total;
}
int fs_delete(const char *path) {
    if (strcmp(path, "/") == 0) {
        printf("错误：不能删除根目录。\n");
        return -1;
    }

    int inode_id = path_lookup(path);
    if (inode_id < 0) {
        printf("错误：路径不存在。\n");
        return -1;
    }

    char parent_path[128], name[128];
    if (path_parent(path, parent_path, name) != 0) {
        printf("路径解析失败。\n");
        return -1;
    }

    int parent_inode_id = path_lookup(parent_path);
    if (parent_inode_id < 0) {
        printf("错误：父目录不存在。\n");
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *target_inode = &inode_table[inode_id];

    // 先做合法性检查（但不释放）
    if (target_inode->type == FILE_TYPE_DIR) {
        for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
            if (target_inode->direct_ptr[i] == 0) continue;
            DirEntry *entries = (DirEntry *)(base + target_inode->direct_ptr[i] * BLOCK_SIZE);
            for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
                if (entries[j].inode_id != 0 &&
                    strcmp(entries[j].name, ".") != 0 &&
                    strcmp(entries[j].name, "..") != 0) {
                    printf("错误：目录非空，不能删除：%s\n", path);
                    return -1;
                }
            }
        }
    }

    // 尝试从父目录中移除该项（先移除！）
    if (dir_remove_entry(parent_inode_id, name) < 0) {
        printf("错误：无法从父目录中移除目录项，终止删除。\n");
        return -1;
    }

    // 现在开始释放 inode 和 block
    if (target_inode->type == FILE_TYPE_FILE) {
        for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
            if (target_inode->direct_ptr[i])
                free_block(target_inode->direct_ptr[i]);
        }
    } else if (target_inode->type == FILE_TYPE_DIR) {
        for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
            if (target_inode->direct_ptr[i])
                free_block(target_inode->direct_ptr[i]);
        }
    }

    free_inode(inode_id);
    printf("删除成功：%s\n", path);
    return 0;
}

int fs_stat(const char *path, FileInfo *info) {
    int inode_id = path_lookup(path);
    if (inode_id < 0) {
        printf("错误：路径不存在：%s\n", path);
        return -1;
    }

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *inode = &inode_table[inode_id];

    info->inode_id = inode_id;
    info->type = (inode->type == FILE_TYPE_DIR) ? 1 : 0;
    info->size = inode->size;

    return 0;
}
int fs_rename(const char *old_path, const char *new_path) {
    if (strcmp(old_path, "/") == 0 || strcmp(new_path, "/") == 0) {
        printf("错误：不能重命名根目录。\n");
        return -1;
    }

    // 1. 拆分新路径
    char old_parent[128], old_name[128];
    char new_parent[128], new_name[128];

    if (path_parent(old_path, old_parent, old_name) != 0 ||
        path_parent(new_path, new_parent, new_name) != 0) {
        printf("路径解析失败。\n");
        return -1;
    }

    // 2. 确保父目录相同（不支持跨目录重命名）
    if (strcmp(old_parent, new_parent) != 0) {
        printf("错误：当前版本不支持跨目录重命名。\n");
        return -1;
    }

    int parent_inode_id = path_lookup(old_parent);
    if (parent_inode_id < 0) {
        printf("父目录不存在。\n");
        return -1;
    }

    int old_inode_id = path_lookup(old_path);
    if (old_inode_id < 0) {
        printf("源路径不存在。\n");
        return -1;
    }

    if (path_lookup(new_path) >= 0) {
        printf("目标名称已存在。\n");
        return -1;
    }

    // 3. 在父目录中查找旧 entry，改名
    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *dir_inode = &inode_table[parent_inode_id];

    for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
        int blk = dir_inode->direct_ptr[i];
        if (blk == 0) continue;

        DirEntry *entries = (DirEntry *)(base + blk * BLOCK_SIZE);
        for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
            if (entries[j].inode_id == old_inode_id &&
                strcmp(entries[j].name, old_name) == 0) {

                strncpy(entries[j].name, new_name, MAX_FILENAME - 1);
                entries[j].name[MAX_FILENAME - 1] = '\0';

                printf("重命名成功：%s -> %s\n", old_path, new_path);
                return 0;
            }
        }
    }

    printf("重命名失败：未找到旧目录项。\n");
    return -1;
}

int alloc_inode() {
    uint8_t *base = (uint8_t *)disk_get_base();
    uint8_t *bitmap = base + super_block.inode_bitmap_start * BLOCK_SIZE;

    int max_inodes = MAX_FILES;

    for (int i = 0; i < max_inodes; ++i) {
        int byte_idx = i / 8;
        int bit_idx  = i % 8;

        if (!(bitmap[byte_idx] & (1 << bit_idx))) {
            // 该位为0，表示空闲，设置为1
            bitmap[byte_idx] |= (1 << bit_idx);
            return i;
        }
    }

    printf("错误：没有可用 inode。\n");
    return -1;
}
int alloc_block() {
    uint8_t *base = (uint8_t *)disk_get_base();
    uint8_t *bitmap = base + super_block.data_bitmap_start * BLOCK_SIZE;

    int max_blocks = super_block.total_blocks - super_block.data_block_start;

    for (int i = 0; i < max_blocks; ++i) {
        int byte_idx = i / 8;
        int bit_idx  = i % 8;

        if (!(bitmap[byte_idx] & (1 << bit_idx))) {
            bitmap[byte_idx] |= (1 << bit_idx);
            return super_block.data_block_start + i;
        }
    }

    printf("错误：没有可用数据块。\n");
    return -1;
}
int path_parent(const char *path, char *parent_out, char *name_out) {
    if (strcmp(path, "/") == 0) {
        printf("错误：根目录没有父目录\n");
        return -1;
    }

    char tmp1[256], tmp2[256];
    strncpy(tmp1, path, sizeof(tmp1));
    strncpy(tmp2, path, sizeof(tmp2));

    char *parent = dirname(tmp1);  // 注意：这两个会修改输入字符串
    char *name   = basename(tmp2);

    strncpy(parent_out, parent, MAX_FILENAME);
    strncpy(name_out, name, MAX_FILENAME);

    return 0;
}
int path_lookup(const char *path) {
    if (strcmp(path, "/") == 0) {
        return 0;  // 根目录 inode 编号是 0
    }

    char path_copy[256];
    strncpy(path_copy, path, sizeof(path_copy));

    // 拆分路径（例如 /a/b/c）
    char *token = strtok(path_copy, "/");
    int current_inode_id = 0;

    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);

    while (token) {
        Inode *inode = &inode_table[current_inode_id];

        if (inode->type != FILE_TYPE_DIR) {
            printf("错误：路径中有非目录部分\n");
            return -1;
        }

        // 遍历当前目录下的每个 DirEntry
        int found = 0;
        for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
            if (inode->direct_ptr[i] == 0) continue;

            DirEntry *entries = (DirEntry *)(base + inode->direct_ptr[i] * BLOCK_SIZE);
            for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
                if (strcmp(entries[j].name, token) == 0) {
                    current_inode_id = entries[j].inode_id;
                    found = 1;
                    break;
                }
            }

            if (found) break;
        }

        if (!found) {
            return -1;  // 没找到对应目录项
        }

        token = strtok(NULL, "/");
    }

    return current_inode_id;
}
int dir_add_entry(int dir_inode_id, const char *name, int inode_id) {
    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *dir_inode = &inode_table[dir_inode_id];

    if (dir_inode->type != FILE_TYPE_DIR) {
        printf("错误：试图向非目录 inode 添加 entry。\n");
        return -1;
    }

    for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
        uint32_t blk = dir_inode->direct_ptr[i];

        // 若该指针为空，先分配一个数据块
        if (blk == 0) {
            int new_blk = alloc_block();
            if (new_blk < 0) {
                printf("错误：目录添加失败，无法分配新块。\n");
                return -1;
            }

            dir_inode->direct_ptr[i] = new_blk;
            dir_inode->size += BLOCK_SIZE;

            blk = new_blk;

            // 注意：新块要初始化为空（全0）
            memset(base + blk * BLOCK_SIZE, 0, BLOCK_SIZE);
        }

        // 遍历当前块中的目录项数组
        DirEntry *entries = (DirEntry *)(base + blk * BLOCK_SIZE);
        for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
            if (entries[j].inode_id == 0 && strlen(entries[j].name) == 0) {
                // 找到空位，写入
                strncpy(entries[j].name, name, MAX_FILENAME);
                entries[j].inode_id = inode_id;

                dir_inode->size += sizeof(DirEntry);
                return 0;
            }
        }
    }

    printf("错误：目录块已满，无法添加更多目录项。\n");
    return -1;
}
int dir_remove_entry(int dir_inode_id, const char *name) {
    void *base = disk_get_base();
    Inode *inode_table = (Inode *)(base + super_block.inode_table_start * BLOCK_SIZE);
    Inode *dir_inode = &inode_table[dir_inode_id];

    if (dir_inode->type != FILE_TYPE_DIR) return -1;

    for (int i = 0; i < INODE_DIRECT_PTRS; ++i) {
        uint32_t blk = dir_inode->direct_ptr[i];
        if (blk == 0) continue;

        DirEntry *entries = (DirEntry *)(base + blk * BLOCK_SIZE);
        for (int j = 0; j < MAX_DENTRY_PER_BLOCK; ++j) {
            if (entries[j].inode_id != 0 && strcmp(entries[j].name, name) == 0) {
                entries[j].inode_id = 0;
                entries[j].name[0] = '\0';
                return 0;
            }
        }
    }

    return -1;
}
void free_inode(int inode_id) {
    uint8_t *bitmap = (uint8_t *)(disk_get_base() + super_block.inode_bitmap_start * BLOCK_SIZE);
    bitmap[inode_id / 8] &= ~(1 << (inode_id % 8));
}
void free_block(int block_id) {
    int rel_block = block_id - super_block.data_block_start;
    uint8_t *bitmap = (uint8_t *)(disk_get_base() + super_block.data_bitmap_start * BLOCK_SIZE);
    bitmap[rel_block / 8] &= ~(1 << (rel_block % 8));
}
